#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
import pickle
import logging
# gae imports
from google.appengine.api import memcache
from google.appengine.ext import db
# gaeo imports
from gaeo.model import DatastoreModel
import settings

def trans(s):
    return s.replace(' ', '_').lower()

EmailPattern = re.compile("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
UrlPattern = re.compile("https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?")

class ForumSerial(DatastoreModel):
    value = db.IntegerProperty(required=True, default=1)

class Counter(DatastoreModel):
    name = db.StringProperty(required=True)
    value = db.IntegerProperty(required=True, default=0)

class User(DatastoreModel):
    account = db.UserProperty(required=True)
    name = db.StringProperty(required=True)
    email = db.EmailProperty(required=True)
    website = db.LinkProperty()
    thumbnail_image = db.StringProperty()
    profile = db.StringProperty(multiline=True)
    role = db.StringProperty(required=True, default="user")
    created = db.DateTimeProperty(auto_now_add=True)
    
    @staticmethod
    def create_key_name(acct):
        """ Returns a datastore key name for an user. """
        return 'user|%s' % acct.email()
    
    @classmethod
    def get_by_username(cls, username):
        u = cls.gql('WHERE name = :name', name=username)
        return u.get()
        
    @property
    def display_name(self):
        return self.name
    
    @property
    def thumbnail_url(self):
        if self.thumbnail_image is None or len(self.thumbnail_image) == 0:
            return '/img/avatar_unknown.png'
        else:
            return settings.THUMBNAIL_URL % self.thumbnail_image
    
    @property
    def is_admin(self):
        return self.role == 'admin'
    
    @property
    def posts_count(self):
        cache_key = 'posts_count|%s' % self.name
        count = memcache.get(cache_key)
        if count is None:
            page = 1
            count = 0
            while True:
                data = db.GqlQuery('SELECT __key__ FROM Topic WHERE author = :author', author=self).fetch(128, (page-1) * 128)
                data_length = len(data)
                count += len(data)
                if data_length < 128:
                    break
                page += 1
            
            memcache.set(cache_key, count, settings.CACHE_TIMEOUT)
        return count
    
    def update(self, **kwds):
        """ update the user settings. """
        if 'email' in kwds and EmailPattern.match(kwds['email']) is not None:
            self.email = db.Email(kwds['email'])
            
        self.profile = kwds.get('profile', '')
        
        if 'website' in kwds and UrlPattern.match(kwds['website']) is not None:
            self.website = db.Link(kwds['website'])
        
        if 'thumb' in kwds and kwds['thumb']:
            self.thumbnail_image = kwds['thumb']
        
        return self.put()
    
class Forum(DatastoreModel):
    """
    Represents a forum entity.
    """
    title = db.StringProperty(required=True, indexed=False)
    summary = db.StringProperty(multiline=True, indexed=False)
    permalink = db.StringProperty(required=True)
    readonly = db.BooleanProperty(required=True, default=False)
    category = db.CategoryProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    
    @classmethod
    def get_by_permalink(cls, permalink):
        query = cls.gql('WHERE permalink = :permalink', permalink=permalink)
        return query.get()
        
    @property
    def topics_count(self):
        cache_key = 'topics_count|%s' % self.permalink
        count = memcache.get(cache_key)
        if count is None:
            counter_name = '%s_topics' % self.permalink
            counters = Counter.gql('WHERE name = :name', name=counter_name).fetch(8)
            count = 0
            for c in counters:
                count += c.value
            memcache.set(cache_key, count, settings.CACHE_TIMEOUT)
        return count
        
    @classmethod
    def get_by_category(cls, category):
        cache_key = 'forums_by_category_%s' % trans(category)
        data = memcache.get(cache_key)
        if data is None:
            query = cls.gql('WHERE category = :category', category=db.Category(category))
            rows = query.fetch(100, 0)
            memcache.set(cache_key, pickle.dumps(rows), settings.CACHE_TIMEOUT)
        else:
            rows = pickle.loads(data)
        return rows
        
    def put(self):
        cache_key = 'forums_by_category_%s' % trans(self.category)
        memcache.delete(cache_key)
        k = super(Forum, self).put()


class Topic(DatastoreModel):
    """
    Represents a topic
    """
    serial = db.IntegerProperty(required=True)
    author = db.ReferenceProperty(User, collection_name="topics")
    forum = db.ReferenceProperty(Forum, collection_name="topics")
    title = db.StringProperty(required=True)
    description = db.TextProperty(required=True)
    last_replied = db.ReferenceProperty(User, collection_name="last_replies")
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    
    @classmethod
    def get_topics_by_forum(cls, forum, limit=100, offset=0):
        cache_key = 'forum|%s|topics' % forum.permalink
        data = memcache.get(cache_key)
        if data is None:
            query = cls.gql('WHERE forum = :forum ORDER BY updated DESC', forum=forum, parent=forum)
            topics = query.fetch(limit, offset)
            memcache.set(cache_key, pickle.dumps(topics), settings.CACHE_TIMEOUT)
        else:
            topics = pickle.loads(data)
        return topics
            
    @property
    def replies_count(self):
        cache_key = 'replies_count|%s|%d' % (self.forum.permalink, self.serial)
        count = memcache.get(cache_key)
        if count is None:
            counter_name = '%s_%d_replies' % (self.forum.permalink, self.serial)
            counters = Counter.gql('WHERE name = :name', name=counter_name).fetch(8)
            count = 0
            for c in counters:
                count += c.value
            memcache.set(cache_key, count, settings.CACHE_TIMEOUT)
        return count
    
    
class Reply(DatastoreModel):
    author = db.ReferenceProperty(User, collection_name="replies")
    topic = db.ReferenceProperty(Topic, collection_name="replies")
    floor = db.IntegerProperty(required=True)
    content = db.TextProperty(required=True)
    likes = db.IntegerProperty(required=True, default=0)
    dislikes = db.IntegerProperty(required=True, default=0)
    replied = db.DateTimeProperty(auto_now_add=True)
    
    @classmethod
    def get_replies_by_topic(cls, topic, limit=500, offset=0):
        cache_key = 'topic_%s_replies' % (topic.key())
        data = memcache.get(cache_key)
        if data is None:
            query = cls.gql('WHERE topic = :topic ORDER BY replied', topic=topic, parent=topic)
            replies = query.fetch(limit, offset)
            memcache.set(cache_key, pickle.dumps(replies), settings.CACHE_TIMEOUT)
        else:
            replies = pickle.loads(data)
        return replies